import math
import random
import logging
from model.graph import Graph



logger = logging.getLogger('Topology')



class Topology(object):
    
    def __init__(self, nodes=[]):
        self.graph = Graph()
        self.nodes = nodes

    def add_node(self, node):
        self.nodes.append(node)

    def add_nodes(self, nodes):
        self.nodes.extend(nodes)

    def grid_distribute(self):
        n = len(self.nodes)
        if n == 0:
            return
        index = 0
        cols = int(math.ceil(math.sqrt(len(self.nodes))))
        rows = (n + cols - 1) // cols
        for i in range(rows):
            for j in range(cols):
                if index < n:
                    if i > 0:
                        idx_top = (i - 1) * cols + j
                        self.graph.add_edge(self.nodes[idx_top], self.nodes[index])
                    if j > 0:
                        idx_left = i * cols + j - 1
                        self.graph.add_edge(self.nodes[idx_left], self.nodes[index])
                    index += 1
                else:
                    break
        self.distribute = f'Grid {cols}x{rows}'
        
    def random_distribute(self, max_degree):
        for node in self.nodes:
            possible_neighbors = [n for n in self.nodes if n != node and node not in self.graph.adj(n)]
            while len(self.graph.adj(node)) == 0:
                num_neighbors = random.randint(1, max_degree)
                neighbors = random.sample(possible_neighbors, min(num_neighbors, len(possible_neighbors)))
                for nb in neighbors:
                    if len(self.graph.adj(nb)) < max_degree and len(self.graph.adj(node)) < max_degree:
                        self.graph.add_edge(node, nb)
        self.distribute = f'Random degree:{max_degree}'
    
    def full_distribute(self):
        for node in self.nodes:
            neighbors = [n for n in self.nodes if n != node and node not in self.graph.adj(n)]
            for nb in neighbors:
                self.graph.add_edge(node, nb)
        self.distribute = f'Full connect'

    def neighbors(self, n):
        return self.graph.adj(n)
    
    def __str__(self):
        s = self.distribute + '\n' + self.graph.__str__()
        return s